/*
 * Copyright 2008 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package sk.gtug.carousel.client;

import java.util.ArrayList;

/**
 * Provides a mechanism for deferred execution of a callback method once all
 * specified Images are loaded.
 */
public class ImageLoader {

	/**
	 * Interface to allow anonymous instantiation of a CallBack Object with
	 * method that gets invoked when all the images are loaded.
	 */
	public interface CallBack {
		void onImagesLoaded(ImageHandle[] imageHandles);
	}

	/**
	 * Static internal collection of ImageLoader instances. ImageLoader is not
	 * instantiable externally.
	 */
	private static ArrayList<ImageLoader> imageLoaders = new ArrayList<ImageLoader>();

	/**
	 * Takes in an array of url Strings corresponding to the images needed to be
	 * loaded. The onImagesLoaded() method in the specified CallBack object is
	 * invoked with an array of ImageHandles corresponding to the original input
	 * array of url Strings once all the images report an onload event.
	 * 
	 * @param urls
	 *            Array of urls for the images that need to be loaded
	 * @param cb
	 *            CallBack object
	 */
	public static void loadImages(String[] urls, CallBack cb) {
		ImageLoader il = new ImageLoader();
		for (int i = 0; i < urls.length; i++) {
			il.addHandle(il.loadImage(urls[i]));
		}
		il.finalize(cb);
		ImageLoader.imageLoaders.add(il);
	}

	private CallBack callBack = null;
	private ArrayList<ImageHandle> images = new ArrayList<ImageHandle>();
	private int loadedImages = 0;
	private int totalImages = 0;

	private ImageLoader() {
	}

	/**
	 * Stores the ImageHandle reference so that when all the images report an
	 * onload, we can return the array of all the ImageHandles.
	 * 
	 * @param img
	 */
	private void addHandle(ImageHandle img) {
		this.totalImages++;
		this.images.add(img);
	}

	/**
	 * Invokes the onImagesLoaded method in the CallBack if all the images are
	 * loaded AND we have a CallBack specified.
	 * 
	 * Called from the JSNI onload event handler.
	 */
	@SuppressWarnings("unused")
	private void dispatchIfComplete() {
		if (callBack != null && isAllLoaded()) {
			callBack.onImagesLoaded(images.toArray(new ImageHandle[0]));
			// remove the image loader
			ImageLoader.imageLoaders.remove(this);
		}
	}

	/**
	 * Sets the callback object for the ImageLoader. Once this is set, we may
	 * invoke the callback once all images that need to be loaded report in from
	 * their onload event handlers.
	 * 
	 * @param cb
	 */
	private void finalize(CallBack cb) {
		this.callBack = cb;
	}

	@SuppressWarnings("unused")
	private void incrementLoadedImages() {
		this.loadedImages++;
	}

	private boolean isAllLoaded() {
		return (loadedImages == totalImages);
	}

	/**
	 * Returns a handle to an img object. Ties back to the ImageLoader instance
	 */
	private native ImageHandle loadImage(String url)/*-{
		// if( callback specified )
		// do nothing

		var img = new Image();
		var __this = this;

		img.onload = function() {
		  if(!img.__isLoaded) {

		    // __isLoaded should be set for the first time here.
		    // if for some reason img fires a second onload event
		    // we do not want to execute the following again (hence the guard)
		    img.__isLoaded = true;       
		    __this.@sk.gtug.carousel.client.ImageLoader::incrementLoadedImages()();
		    img.onload = null;

		    // we call this function each time onload fires
		    // It will see if we are ready to invoke the callback
		    __this.@sk.gtug.carousel.client.ImageLoader::dispatchIfComplete()();   
		  } else {
		    // we invoke the callback since we are already loaded
		    __this.@sk.gtug.carousel.client.ImageLoader::dispatchIfComplete()();   
		  }
		}

		img.src = url;

		return img;
	}-*/;
}
